package com.lvcoding.miaoshademo.controller;

import com.google.common.util.concurrent.RateLimiter;
import com.lvcoding.miaoshademo.service.StockService;
import com.lvcoding.miaoshademo.service.UserService;
import com.lvcoding.miaoshademo.util.Res;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @description 描述
 * @date   2022-01-02 下午4:38
 * @author  wuyanshen
 */
@Slf4j
@RequestMapping("stock")
@RestController
public class StockController {


    @Autowired
    private StockService stockService;
    @Autowired
    private UserService userService;

    // 创建令牌桶实例
    private RateLimiter rateLimiter = RateLimiter.create(100);

    @GetMapping("kill")
    public Res kill(Integer id) {
        log.info("秒杀商品的id={}", id);

        try {
            // 使用悲观锁解决超卖
            synchronized(this) {
                // 根据秒杀商品的id调用秒杀业务
                Integer orderId = stockService.kill(id);
                return Res.success("秒杀成功，订单id=" + orderId);
            }

        } catch (Exception e) {
            e.printStackTrace();
            return Res.fail(e.getMessage());
        }

    }


    // 开发一个秒杀方法，乐观锁防止超卖 + 令牌桶算法限流 + md5签名(hash签名，接口隐藏)
    @GetMapping("killtoken")
    public Res killtoken(Integer id, Integer userId, String md5) {

        // 1.没有获取到token请求一直知道获取到token令牌
        log.info("等待的时间" + rateLimiter.acquire());

        // 2.设置一个等待时间，如果在等待时间内获取到了token令牌，则处理业务，如果在等待时间内没有获取到响应token则抛弃
        if(!rateLimiter.tryAcquire(3, TimeUnit.SECONDS)) {
            log.info("当前请求被限流，直接抛弃，无法调用后续秒杀逻辑...");
            return Res.fail("抢购失败，请重试");
        }

        try {
            // 根据秒杀商品的id调用秒杀业务
            Integer orderId = stockService.kill(id, userId, md5);
            if (orderId != null) {
                return Res.success("秒杀成功，订单id=" + orderId);
            } else {
                return Res.fail("秒杀失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return Res.fail(e.getMessage());
        }
    }

    // 开发一个秒杀方法，乐观锁防止超卖 + 令牌桶算法限流 + md5签名(hash签名，接口隐藏) + 单用户访问频率限制
    @GetMapping("killtokenLimit")
    public Res killtokenLimit(Integer id, Integer userId, String md5) {

        // 1.没有获取到token请求一直知道获取到token令牌
        log.info("等待的时间" + rateLimiter.acquire());

        // 2.设置一个等待时间，如果在等待时间内获取到了token令牌，则处理业务，如果在等待时间内没有获取到响应token则抛弃
        if(!rateLimiter.tryAcquire(3, TimeUnit.SECONDS)) {
            log.info("当前请求被限流，直接抛弃，无法调用后续秒杀逻辑...");
            return Res.fail("抢购失败，请重试");
        }

        try {

            // 单用户调用接口频率限制
            int count = this.userService.saveUserCount(userId);
            log.info("用户截止该次访问的次数[{}]", count);

            // 进行调用次数的限制
            boolean isBanned = this.userService.getUserCount(userId);
            if(isBanned) {
                log.info("抢购失败，超过请求频率限制");
                return Res.fail("抢购失败，超过请求频率限制");
            }


            // 根据秒杀商品的id调用秒杀业务
            Integer orderId = stockService.kill(id, userId, md5);
            if (orderId != null) {
                return Res.success("秒杀成功，订单id=" + orderId);
            } else {
                return Res.fail("秒杀失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return Res.fail(e.getMessage());
        }
    }

    @GetMapping("getMd5")
    public Res getMd5(Integer userId, String goodId) {
        String md5 = this.stockService.getMd5(userId, goodId);
        return Res.success(md5);
    }

    /**
     * 开始秒杀活动
     *
     * @param goodId 商品id
     * @param time   活动持续时间，单位秒
     * @return Res
     * @date 2022/1/11 下午5:39
     * @author wuyanshen
     */
    @GetMapping("startMiaosha")
    public Res startMiaosha(Integer goodId, Long time) {
        this.stockService.startMiaosha(goodId, time);
        return Res.success();
    }
}
